﻿#ifndef _slt_csConnect_h_20220712
#define _slt_csConnect_h_20220712

#include "SLT_tool_pub.h"

#define  _LOG_slcs_connect_impl_debug(_id,_format,...)  SLT_LOG_DEBUG("[slcs](%u)" _format,_id, ##__VA_ARGS__)
#define  _LOG_slcs_connect_impl_info(_id,_format,...)  SLT_LOG_INFO("[slcs](%u)" _format,_id, ##__VA_ARGS__)
#define  _LOG_slcs_connect_impl_err(_id,_format,...)  SLT_LOG_ERR("[slcs](%u)" _format,_id, ##__VA_ARGS__)
/*
本类实现 客户端 到 服务端 的长链接。 内部实现连接，心跳，和数据的收发校验
调用者实现消息定义和数据的传输
连接接口调用时序
	   客户端                                        服务端
	SLCS_connect         -> SLCS_onSendConnect     ->     SLCS_recvConnectMsg -> SLCS_respConnect
	SLCS_onConnectResult <- SLCS_recvConnectMsg    <-     SLCS_OnSendConnectMsg     <-
数据接口调用时序:
	SLCS_SendReqData     -> SLCS_OnSendDataMsg     ->     SLCS_getRecvReqData
						  SLCS_getRecvRespData     <-     SLCS_OnSendDataMsg  <-  SLCS_SendReqData/SLCS_sendRespData
*/


//v2v c-s 连接协议
//msgtype: _proxy_msg_context_type
#define	_slcs_msg_context_type_connectreq    1
#define	_slcs_msg_context_type_connectresp   2
#define	_slcs_msg_context_type_heartreq      3

#pragma pack(1)
struct _slcs_msg_v2v_commHead
{
	uint8_t      msgtype;  	//_proxy_msg_context_type
	slt_uuid_t   uuid;
	uint32_t     index;
	uint32_t     len;   	//消息长度 包括头部
};
//连接请求
struct _slcs_msg_v2v_connectReq_st
{
	_slcs_msg_v2v_commHead head;
};
//连接应答
struct _slcs_msg_v2v_connectResp_st
{
	_slcs_msg_v2v_commHead head;
	uint16_t     errcode;  //错误消息
};
//心跳，心跳为双向心跳，没有应答
struct _slcs_msg_v2v_heartbeat_st
{
	_slcs_msg_v2v_commHead head;
};
#pragma pack()

class CSLCS_connectImpl
{
public:
	enum cs_connect_mode
	{
		cs_connect_mode_c,  //主动连接，客户端
		cs_connect_mode_s   //被动连接，服务端
	};
	//连接状态维护：处理连接和心跳
	enum psc_work_state
	{
		psc_work_state_init,
		psc_work_state_conning,
		psc_work_state_ok
	};
	struct slcs_resendData : public SLT_resendListUserData
	{
		int   m_msgid = 0;
		SLT_memBuf  m_mem;
	};
	typedef std::shared_ptr<slcs_resendData>  slcs_resendDataStpr;
	struct _slcs_recvCheckUserData : public SLT_recvRepeatCheckUserData
	{
		int  m_respMsgID;
	};
	typedef std::shared_ptr<_slcs_recvCheckUserData>  _slcs_recvCheckUserDataSptr;
	enum pcs_event
	{
		//pcs_event_connect_ok,
		//pcs_event_connect_timerout,    //连接超时
		pcs_event_timeout_disconnect = 1  //超时断开
	};

	class CSLCS_connectInnerImpl : public SLT_resendListCB
	{
	public:
		virtual void SLT_RLResend(SLT_resendList* _plist, SLT_resendItemCL* pitem) { m_powner->_SLCS_RLResend(_plist, pitem); }
		virtual void SLT_RLTimeOut(SLT_resendList* _plist, SLT_resendItemCL* pitem) { m_powner->_SLCS_RLTimeOut(_plist, pitem); }
		CSLCS_connectImpl* m_powner{ nullptr };
	};
public:
	CSLCS_connectImpl()
	{
		m_innerImpl.m_powner = this;
		m_slcs_connectResendlist.setCB(&m_innerImpl);
		m_slcs_dataResendList.setCB(&m_innerImpl);
	}
	void SLCS_checkState() {
		if (m_workState == psc_work_state_init) {
			return;
		}
		if (m_checkTimer.elapseMs() < 1000) {
			return;
		}
		m_checkTimer.start();

		if (m_workState == psc_work_state_ok) {
			//检测心跳超时
			if (m_lastrecvtimer.elapseMs() > m_disconnecttime) {
				m_workState = psc_work_state_init;  //外面需要正确处理断开事件
				SLCS_OnEvent(pcs_event_timeout_disconnect);
				return;
			}

			//发送心跳
			if (m_lastsendtimer.elapseMs() > m_heartInterval) {
				m_lastsendtimer.start();
				_SLCS_sendheart();
			}
		}

		m_slcs_connectResendlist.check();
		m_slcs_dataResendList.check();
		m_slcs_recvCheck.checkState();
	}
	void SLCS_reset() {
		m_workState = psc_work_state_init;
		memset(&m_slcs_uuid, 0, sizeof(m_slcs_uuid));
		m_slcs_connectRespCache.clear();
		m_slcs_dataResendList.clear();
		m_slcs_connectResendlist.clear();
		m_slcs_recvCheck.clear();
		m_remoteSId = 0;
		if (m_workMode == cs_connect_mode_s) {
			m_remoteSender.reset();
		}
	}
	void SLCS_initEnv(int _localSid, int _connectMsgID, uint32_t _id)
	{
		m_cslocalSId = _localSid;
		m_connectMsgID = _connectMsgID;
		m_slcs_id = _id;
	}
public:
	//主动方
	void 			SLCS_connect(const char* _pdata = nullptr, int _len = 0)   //开始连接
	{
		if (m_workState != psc_work_state_init) {
			return;
		}
		//产生guid,
		slt_uuid_create(&m_slcs_uuid);

		slcs_resendData* presenddata = new slcs_resendData();

		//协议头 index
		_slcs_msg_v2v_connectReq_st req;
		req.head.uuid = m_slcs_uuid;
		req.head.index = m_slcs_msgidBase++;
		req.head.msgtype = _slcs_msg_context_type_connectreq;
		req.head.len = sizeof(req) + _len;
		
		presenddata->m_mem.append((char*)&req, sizeof(req));
		if (_pdata) {
			presenddata->m_mem.append(_pdata, _len);
		}
		//缓存
		m_slcs_connectResendlist.add2(req.head.index, 0, 1000, -1, SLT_resendListUserDataSptr(presenddata));

		m_workMode = cs_connect_mode_c;
		m_workState = psc_work_state_conning;
		_LOG_slcs_connect_impl_info(m_slcs_id,"connect req,index:%u,guid:%s",req.head.index,slt_uuid_to_string(&req.head.uuid).c_str());
		SLCS_onSendConnect(presenddata->m_mem);
	}
	virtual  void  	SLCS_onSendConnect(SLT_memBuf& _mem) {};       //发送连接消息
	virtual  void  	SLCS_onConnectResult(int _errocode, char* pdata, int _len) {};  //连接结果通知
public:
	//服务方
	virtual  void   SLCS_OnConnectReset(){}  //接收端连接复位
	virtual  void  	SLCS_onRecvConnect(SLF_senderPtr& _sender, uint32_t _srcSid, int _msgid, const char* _pdata, int _len) {};  //接收到连接消息通知
	void     		SLCS_respConnect(int _errocode, char* pdata, int _len)    //应答连接结果
	{
		if (m_workState != psc_work_state_conning) {
			return;
		}
		m_workState = psc_work_state_ok;

		//服务方: 应答连接结果
		_slcs_msg_v2v_connectResp_st resp;
		resp.head.index = m_slcs_connectIndex;
		resp.head.msgtype = _slcs_msg_context_type_connectresp;
		resp.head.uuid = m_slcs_uuid;
		resp.head.len = sizeof(resp) + _len;
		resp.errcode = _errocode;

		//缓存应答结果
		m_slcs_connectRespCache.resize(sizeof(resp) + _len);
		m_slcs_connectRespCache.add((char*)&resp, sizeof(resp));
		m_slcs_connectRespCache.add(pdata, _len);  //内部处理为0的情况

		SLCS_OnSendConnectMsg(m_slcs_connectRespCache);
	}
public:
	void  			SLCS_recvConnectMsg(SLF_senderPtr& _sender, uint32_t srcSid, int msgid, SLF_sendMemBasePtr& _data) //接收到消息：连接请求,连接应答，心跳
	{
		//根据状态过滤
		_slcs_msg_v2v_commHead* phead = (_slcs_msg_v2v_commHead*)_data->getData();
		if (phead->msgtype == _slcs_msg_context_type_connectreq) {
			//接收到连接请求
			_SLCS_recvConnectReq(_sender, srcSid, msgid, _data);
		}
		else if (phead->msgtype == _slcs_msg_context_type_connectresp) {
			//接收到连接应答
			_SLCS_recvConnectResp(_sender, srcSid, msgid, _data);
		}
		else if (phead->msgtype == _slcs_msg_context_type_heartreq) {
			_slcs_msg_v2v_heartbeat_st* hearthead = (_slcs_msg_v2v_heartbeat_st*)_data->getData();
			if (m_slcs_uuid != hearthead->head.uuid) {
				_LOG_slcs_connect_impl_err(m_slcs_id, "recv heart uuid error,u1:%s,u2:%s", slt_uuid_to_string(&m_slcs_uuid).c_str(), slt_uuid_to_string(&hearthead->head.uuid).c_str());
				return;
			}
			m_lastrecvtimer.start();
			if (m_workMode == cs_connect_mode_s) {
				m_remoteSender = _sender;
			}
			_LOG_slcs_connect_impl_info(m_slcs_id, "recv heart");
		}
		else {
			_LOG_slcs_connect_impl_err(m_slcs_id, "msgtype error:%d", phead->msgtype);
			return;
		}
	}
	virtual void  	SLCS_OnSendConnectMsg(SLT_memBuf& _mem)   //发送连接消息：连接应答，心跳
	{
		//将消息发送到远端
		if (m_cslocalSId == 0 || m_connectMsgID == 0) {
			_LOG_slcs_connect_impl_err(m_slcs_id, "send SendConnectMsg evn error");
			return;
		}
		//_LOG_slcs_connect_impl_err(m_slcs_id,"send SendConnectMsg to remote");
		//SLF_sendMemBasePtr membuf = std::make_shared<SLF_sendMemBase>();
		SLF_sendMemBasePtr membuf(new SLF_sendMemBase());
		membuf->m_mem = _mem;
		m_remoteSender->SLF_postMsg(m_remoteSId, m_connectMsgID, m_cslocalSId, membuf);
	}
	virtual void  	SLCS_OnEvent(pcs_event _ev, const char* pdata = nullptr, int _len = 0) = 0;
public:
	virtual void   SLCS_OnSendDataMsg(int _msgid, SLT_memBuf& _memBuf)   //发送数据消息
	{
		if (0 == m_cslocalSId) {
			_LOG_slcs_connect_impl_err(m_slcs_id, "error,m_cslocalSId = 0");
			return;
		}
		//_slcs_msg_v2v_commHead *head = (_slcs_msg_v2v_commHead*)_memBuf.get();
		//_LOG_slcs_connect_impl_err(m_slcs_id,"send data to remote,msgid:%d,index:%d,len:%d,memlen:%d",_msgid,head->index,head->len,_memBuf.getLen());

		//SLF_sendMemBasePtr membuf = std::make_shared<SLF_sendMemBase>();
		SLF_sendMemBasePtr membuf(new SLF_sendMemBase());
		membuf->m_mem = _memBuf;
		m_remoteSender->SLF_postMsg(m_remoteSId, _msgid, m_cslocalSId, membuf);
	}
	int            SLCS_sendReqData(int _msgid, const std::string& _strtype, int _intervalMs, int _timeoutMs, const char* _pdata, int _len, uint32_t* _req_index = nullptr, bool _resend = true)
	{
		//_strtype  使用字符串，解决一个消息发送不同内容的场景，  
		//_resend 表示消息不重发，只发送一次
		if (m_workState != psc_work_state_ok) {
			return -1;
		}
		if (_resend) {
			m_slcs_dataResendList.delByType(_strtype);
		}

		_slcs_msg_v2v_commHead head;
		head.index = m_slcs_msgidBase++;
		head.len = sizeof(head) + _len;
		head.msgtype = 0;
		head.uuid = m_slcs_uuid;
		if (_req_index) {
			*_req_index = head.index;
		}

		SLT_memBuf membuf;
		membuf.resize(sizeof(head) + _len);
		membuf.append((char*)&head, sizeof(head));
		membuf.append(_pdata, _len);

		//缓存，重发
		if (_resend) {
			slcs_resendDataStpr udSptr = std::make_shared<slcs_resendData>();
			udSptr->m_mem = membuf;
			udSptr->m_msgid = _msgid;
			m_slcs_dataResendList.add3(head.index, _strtype, _intervalMs, _timeoutMs, udSptr);
		}
		//_LOG_slcs_connect_impl_debug(m_slcs_id,"send data, index:%d,len:%d,memlen:%d",head.index,head.len,membuf.getLen());
		SLCS_OnSendDataMsg(_msgid, membuf);
		return 0;
	}
	int            SLCS_getRecvReqData(SLF_sendMemBasePtr& _in_data, uint32_t& _req_index, char*& _out_pdata, int& _out_len)  //获取接收的请求数据
	{
		//接收去重
		_slcs_msg_v2v_commHead* phead = (_slcs_msg_v2v_commHead*)_in_data->getData();
		if (phead->len != _in_data->getLen()) {
			_LOG_slcs_connect_impl_err(m_slcs_id, "req len error,%d:%d", phead->len, _in_data->getLen());
			return -1;
		}
		if (phead->uuid != m_slcs_uuid) {
			_LOG_slcs_connect_impl_err(m_slcs_id, "req uuid error");
			return -2;
		}
		if (0 != phead->index) {
			SLT_recvRepeatCheckUserDataSptr userdata;
			m_slcs_recvCheck.checkRecv(phead->index, userdata);
			if (userdata) {
				//重复接收
				_slcs_recvCheckUserData* pud = (_slcs_recvCheckUserData*)userdata.get();
				SLCS_OnSendDataMsg(pud->m_respMsgID, userdata->m_data);
				return -3;
			}
		}

		if (phead->len != sizeof(_slcs_msg_v2v_commHead)) {
			_out_pdata = _in_data->getData() + sizeof(_slcs_msg_v2v_commHead);
			_out_len = phead->len - sizeof(_slcs_msg_v2v_commHead);
		}
		_req_index = phead->index;
		return 0;
	}
	int            SLCS_sendRespData(uint32_t& _req_index, int _respMsgID, const char* _pdata, int _len)   //接收后发送的应答数据
	{
		_slcs_msg_v2v_commHead head;
		head.index = _req_index;
		head.len = sizeof(head) + _len;
		head.msgtype = 0;
		head.uuid = m_slcs_uuid;

		SLT_memBuf membuf;
		membuf.resize(sizeof(head) + _len);
		membuf.append((char*)&head, sizeof(head));
		membuf.append(_pdata, _len);

		_slcs_recvCheckUserDataSptr udSptr = std::make_shared<_slcs_recvCheckUserData>();
		udSptr->m_respMsgID = _respMsgID;
		udSptr->m_data = membuf;
		m_slcs_recvCheck.save2(_req_index, udSptr);

		SLCS_OnSendDataMsg(_respMsgID, membuf);
		return 0;
	}
	int            SLCS_getRecvRespData(SLF_sendMemBasePtr& _in_data, uint32_t* _req_index, char*& _out_pdata, int& _out_len, slcs_resendDataStpr* udSptr = nullptr)  //获取接收的应答数据
	{
		_slcs_msg_v2v_commHead* phead = (_slcs_msg_v2v_commHead*)_in_data->getData();
		if (phead->len != _in_data->getLen()) {
			_LOG_slcs_connect_impl_err(m_slcs_id, "resp len error");
			return -1;
		}
		if (phead->uuid != m_slcs_uuid) {
			_LOG_slcs_connect_impl_err(m_slcs_id, "resp uuid error");
			return -2;
		}
		SLT_resendItemCL* pitem = m_slcs_dataResendList.find(phead->index);
		if (!pitem) {
			return -3;
		}
		if (udSptr) {
			*udSptr = std::static_pointer_cast<slcs_resendData>(pitem->m_userdata);
		}
		m_slcs_dataResendList.del(phead->index);
		if (_req_index) {
			*_req_index = phead->index;
		}

		if (phead->len != sizeof(_slcs_msg_v2v_commHead)) {
			_out_pdata = _in_data->getData() + sizeof(_slcs_msg_v2v_commHead);
			_out_len = phead->len - sizeof(_slcs_msg_v2v_commHead);
		}
		return 0;
	}

public:
	void _SLCS_RLResend(SLT_resendList* _plist, SLT_resendItemCL* pitem) {
		if (&m_slcs_connectResendlist == _plist) {
			slcs_resendData* prd = (slcs_resendData*)pitem->m_userdata.get();
			_slcs_msg_v2v_connectReq_st* preq = (_slcs_msg_v2v_connectReq_st*)prd->m_mem.get();
			_LOG_slcs_connect_impl_info(m_slcs_id,"connect req resend,index:%u,guid:%s",preq->head.index,slt_uuid_to_string(&preq->head.uuid).c_str());
			SLCS_onSendConnect(prd->m_mem);
		}
		else {
			slcs_resendData* prd = (slcs_resendData*)pitem->m_userdata.get();
			SLCS_OnSendDataMsg(prd->m_msgid, prd->m_mem);
		}
	}
	void _SLCS_RLTimeOut(SLT_resendList* _plist, SLT_resendItemCL* pitem) {}
protected:
	void   _SLCS_sendheart() {
		//心跳不用应答。双向心跳，对方主动发送心跳
		_slcs_msg_v2v_heartbeat_st heartreq;
		heartreq.head.index = m_slcs_msgidBase++;
		heartreq.head.msgtype = _slcs_msg_context_type_heartreq;
		heartreq.head.uuid = m_slcs_uuid;
		heartreq.head.len = sizeof(heartreq);

		_LOG_slcs_connect_impl_info(m_slcs_id, "send heart:uuid:%s", slt_uuid_to_string(&heartreq.head.uuid).c_str());
		SLT_memBuf membuf;
		membuf.add((char*)&heartreq, sizeof(heartreq));
		SLCS_OnSendConnectMsg(membuf);
	}
	void   _SLCS_recvConnectReq(SLF_senderPtr& _sender, uint32_t srcSid, int msgid, SLF_sendMemBasePtr& _data)   //接收到连接消息
	{
		//服务方: 接收到连接消息
		m_lastrecvtimer.start();
		_slcs_msg_v2v_connectReq_st* preq = (_slcs_msg_v2v_connectReq_st*)_data->getData();
		if (preq->head.len > _data->getLen()) {
			_LOG_slcs_connect_impl_err(m_slcs_id, "connect recv len error,guid:%s", slt_uuid_to_string(&preq->head.uuid).c_str());
			return;
		}

		if (m_workState != psc_work_state_init) {
			if (m_slcs_uuid != preq->head.uuid) {
				_LOG_slcs_connect_impl_info(m_slcs_id,"connect recv,reset,index:%u,guid:%s",m_slcs_connectIndex,slt_uuid_to_string(&m_slcs_uuid).c_str());
				SLCS_reset(); //guid不同。重新开始
				SLCS_OnConnectReset();
			}else{
				//命令去重
				if (m_slcs_connectRespCache.getLen() > 0) {
					//发送应答消息
					_LOG_slcs_connect_impl_info(m_slcs_id,"connect recv,cache resp,index:%u,guid:%s",m_slcs_connectIndex,slt_uuid_to_string(&m_slcs_uuid).c_str());
					SLCS_OnSendConnectMsg(m_slcs_connectRespCache);
				}else{
					_LOG_slcs_connect_impl_err(m_slcs_id,"connect recv,no resp,index:%u,guid:%s",m_slcs_connectIndex,slt_uuid_to_string(&m_slcs_uuid).c_str());
				}
				return;
			}
		}
		
		m_slcs_connectIndex = preq->head.index;
		m_slcs_uuid = preq->head.uuid;
		m_remoteSender = _sender;
		m_remoteSId = srcSid;
		_LOG_slcs_connect_impl_info(m_slcs_id,"connect recv,index:%u,guid:%s",m_slcs_connectIndex,slt_uuid_to_string(&m_slcs_uuid).c_str());
		m_workState = psc_work_state_conning;  //接收连接中，等待应用层应答
		if (preq->head.len == sizeof(_slcs_msg_v2v_connectReq_st)) {
			SLCS_onRecvConnect(_sender, srcSid, msgid, nullptr, 0);
		}
		else {
			SLCS_onRecvConnect(_sender, srcSid, msgid, _data->getData() + sizeof(_slcs_msg_v2v_connectReq_st), preq->head.len - sizeof(_slcs_msg_v2v_connectReq_st));
		}

	}
	void   _SLCS_recvConnectResp(SLF_senderPtr& _sender, uint32_t srcSid, int msgid, SLF_sendMemBasePtr& _data)  //接收到连接应答
	{
		//发起方: 接收到连接应答
		_slcs_msg_v2v_connectResp_st* presp = (_slcs_msg_v2v_connectResp_st*)_data->getData();
		if (presp->head.len < _data->getLen()) {
			_LOG_slcs_connect_impl_err(m_slcs_id, "connect resp len error");
			return;
		}
		if (m_slcs_uuid != presp->head.uuid) {
			_LOG_slcs_connect_impl_err(m_slcs_id, "connect resp uuid error");
			return;
		}
		m_lastrecvtimer.start();

		SLT_resendItemCL* pitem = m_slcs_connectResendlist.find(presp->head.index);
		if (!pitem) {
			_LOG_slcs_connect_impl_err(m_slcs_id, "connect resp error,index no exsit:%u",presp->head.index);
			return;
		}
		_LOG_slcs_connect_impl_info(m_slcs_id, "connect resp index:%u",presp->head.index);
		m_slcs_connectResendlist.del(presp->head.index);

		m_workState = psc_work_state_ok;
		m_remoteSId = srcSid;
		if (sizeof(_slcs_msg_v2v_connectResp_st) == presp->head.len) {
			SLCS_onConnectResult(presp->errcode, nullptr, 0);
		}
		else {
			SLCS_onConnectResult(presp->errcode, _data->getData() + sizeof(_slcs_msg_v2v_connectResp_st), _data->getLen() - sizeof(_slcs_msg_v2v_connectResp_st));
		}
	}
public:
	cs_connect_mode  m_workMode = cs_connect_mode_s;
	psc_work_state  m_workState = psc_work_state_init;

	SLT_resendList       m_slcs_connectResendlist;   //连接重传
	SLT_resendList       m_slcs_dataResendList;      //数据重传
	SLT_recvRepeatCheck  m_slcs_recvCheck;           //接收请求去重

	uint32_t        m_slcs_connectIndex{ 0 };
	SLT_memBuf      m_slcs_connectRespCache;  //连接应答缓存
	slt_uuid_t      m_slcs_uuid;
	uint32_t        m_slcs_msgidBase{ 1 };
	uint32_t        m_slcs_id{ 0 };   //对象id，日志使用

	//心跳为双向心跳,有数据发送，间隔内则不再发送心跳
	int              m_disconnecttime = 30000;   //超时连接断开时间
	int              m_heartInterval = 5000;     //心跳5s一次
	SLT_elapseTimer  m_lastrecvtimer;         //最后接收数据的时间
	SLT_elapseTimer  m_lastsendtimer;         //最后发送数据的时间
	SLT_elapseTimer  m_heartTimer;            //心跳检查时间
	SLT_elapseTimer  m_checkTimer;            //检测间隔

	SLF_senderPtr    m_remoteSender;   //远端连接。在服务模式下，sender会发生变化。需要通过命令进行必要的更新
	uint32_t         m_remoteSId{ 0 };
	uint32_t         m_cslocalSId{ 0 };    //本地sessionid
	int              m_connectMsgID{ 0 };  //转发连接消息的消息id, 对应调用 SLCS_recvConnectMsg

	CSLCS_connectInnerImpl  m_innerImpl;
};

#endif  //_slt_csConnect_h_20220712